2015-07-29

創用 CC 授權條款

Flow Control:程式流程控制:

前面把所有R的基本指令都介紹完了,

相信有不少人應該可以自豪的說:學了R以後,

不過……

難道這樣就讓大家等吃便當嗎?

順便tag今天的課程說現在的講師太混,那也真的是太對不起你們和其他講者

讓我們來寫一些 R 的程式吧!~

對了,

如果課程中有任何問題都可以問助教
R社群最神奇的地方就是:







助教什麼都會!




ps: 在console命令列中輸入?指令,可以看到該指令詳細的說明,例如?rbind,這時候就會出現相關的資料。 在console命令列中輸入example(指令),變可以看到該指令用法,例如:example(rbind),這時候console就會出現相關的訊息。

程式前言

程式的概念:

程式,或說是程式語言,名詞中既然既然已經有語言這個名詞出現,就表示這種語言基本上和一般中文、英文等語言一定有類似的地方。

像是以前學英文常說的英文有八大詞性,五大句型…..其實電腦程式語言也有類似的用法。

一個基本的程式,一般除了變數、資料之外 ,還有許多的指令來協助程式的完成。
簡單說,寫一個程式,其實和寫一段文章,或是一篇小說差不多。

範例:葉丙成老師的BJT Online

柏拉圖有一天問蘇格拉底:什麼是愛情?

but…


今天不是來教大家學英文,

也不是來講機率或翻轉教育課程,

所以下面還是來學R吧

Syllabus

Syllabus1:

判斷條件控制

  • if() / else()
  • ifelse()
  • switch()
    測試:閏年

迴圈控制

  • for(),
  • while(),
  • repeat() / break,
    範例:計算一段時間內有多少閏年

Syllabus2:

Function

  • 編輯撰寫一個R程式

Debug and Error Handling

  • 除錯(找蟲)和程式錯誤管理

Flow Control:程式流程控制:

If Loop

if / else

Syntex:

if (condition_1){
    #Do something here....
} else if (conditon_2){
    #Do something here
} else {
    #Do something here
}

Note: else if and else are optional.

==> 請看 swirl 的範例

ifelse()

使用方法:

ifelse(test_cond1, res_yes, res_no) 若test_cond1的條件成立(回傳是TRUE),則會執行res_yes的動作,否則test_cond1回傳是FALSE的話,則會執行res_no 例如

x = 7
ifelse(x>10, print("x大於10"), print("x小於10"))
## [1] "x小於10"
## [1] "x小於10"

==> 請看 swirl 的範例

if()/else() 邏輯判斷符號

  • R支援的邏輯判斷符號如下:
  • "==":等於
  • "<=":小於等於
  • ">=":大於等於
  • ">":大於
  • "<":小於
  • "!=":不等於
  • "&":邏輯中的「且」 或「and」
  • "|":邏輯中的「或」 或「or」

if()/else()ifelse() 的差別

一般情況下,如果向量中只有一個變數時,if()和ifelse()沒什麼差別, 但如果向量是一個多個數值的陣列array,矩陣matrix,或是list/data.frame時,這時候就很難用if來判斷了。 例如:

x = c(1:10)
if (x>5)
  print("x>5")
## Warning in if (x > 5) print("x>5"): the condition has length > 1 and only
## the first element will be used
x = c(1:10)
ifelse((x>5),TRUE,FALSE)
##  [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE

==> 請看 swirl 的範例

那會了if,可以怎麼用?

小測驗:閏年

情境:小明的故事一

小明得到媽媽的鼓勵和更多的糖果之後,就開開心心的去學校了。 上學途中,看到滿天飛舞的櫻花,不禁心頭一陣酸楚,之前拿到的糖果此時居然愈來愈重,吃了之後也不覺得甜美。 邊走邊想著:小時候喜歡的那個女孩,也是曾經在這種爛漫的的櫻花中,含著眼淚吞著鼻水的互相告別,不知道現在怎麼樣了? 對了,記得她是閏年出生的,那閏年的意義對她應該很重要吧。我們兩個人年紀都大了,已經不是3歲5歲的小孩了,我還能去找她嗎?還有機會嗎?還可以在一起嗎?

這時候,小明下定決心,想要認真的面對這段以前甜美,現在遺憾的回憶,打算在閏年的時候,鼓起勇氣去找她。 便在路旁的星巴八飲料店,拿出優雅的水果電腦,很快的在電腦上用R寫了一個判斷閏年的小工具。

原來如此。小明突然覺得自己很厲害

請判斷輸入的年份是不是閏年?


目前使用的格里曆閏年規則如下:


西元年份除以400可整除=閏年。

西元年份除以4可整除但除以100不可整除=閏年。

西元年份除以4不可整除=平年。

西元年份除以100可整除但除以400不可整除=平年


https://zh.wikipedia.org/zh-tw/%E9%97%B0%E5%B9%B4

case 1: if(), else(), %%, ==

year1 = 2015  #或是可以用 as.integer(readline("input year:")),來輸入想要的數值
if ((year1 %% 4)==0){
  if ((year1 %% 100)==0){
    if ((year1 %% 400)==0)
      print("Leap year")
    else
      print("Not leap year")
  } else {
    print("Leap year")
  }
} else {
  print("Not leap year")
}
## [1] "Not leap year"

case2: 運用邏輯符號的"!"(not),"&"(and),以及"

"(or)

year1 = 2015  #或是可以用 as.integer(readline("input year:")),來輸入想要的數值
if ( ((!(year1 %% 4)) && (year1 %%100)) || (!(year1 %% 400)) ){  
  print("Leap year")
} else {  
  print("Not leap year")  
}
## [1] "Not leap year"

迴圈

for()

for()

for() 迴圈簡單說就是某個動作重複做多少次,一般來說,需要明確的知道

會了for,可以怎麼用?

這是copy-paste沒錯

小測驗2:算有多少個閏年

情境:小明的故事二

小明又出現了,這次他帶著更多的遺憾,更多的苦澀,回到家裡。 可是,小明他自己知道,小明這個名字是以前國立編譯館,眾多國中國小出版社,PTT八卦版許多夢境中最常被拿出來用的名字, 「小明」這兩個字充滿著正面、希望、勇氣、努力,根本就是各種可能的代名詞。連小明的日文名字:Hikari或Akira,都是史詩巨作:棋靈王主角的名字。 「我不能如此放棄」,小明如此的對自己這麼說。

這麼想的小明,腦中開始的一連串的計劃了。首先,小明第一件要做的事情,毫無疑問的就是要知道,到底有多少年是閏年。

這時候路旁的小七便利店,突然360度翻了過來,瞬間變成一家星巴八飲料店的外觀, 小七店內突然傳出聲響:Change,仔細一看,小七店員身上的招牌紅色T-Shirt,居然也變成星巴八的白衣綠裙。

天時地利人和,小明毫不猶豫的,拿起手上的水果電腦,再次走進星巴八內,誓言如果沒辦法用R寫出計算閏年的程式的話就不出來。

請說明輸入一段時間內(如1000~2015),有幾年是閏年?並列出分別是哪幾年?

除了上面的判斷是不是閏年之外,麻煩請在想一想,如何把一段時間內,例如從西元1000至西元2015年到底有多少年是閏年,分別為哪幾年?

提醒:可以用for來持續檢查某一年是否是閏年,例如:開始年:1000,結束年:2015

for(任意變數 in 開始:結束){ 打算要重複執行的程式 }

case1: 使用上面的判斷式

yearS = 1980 # 或是可以輸入:as.integer(readline("input start year:")) 來自己輸入開始年
yearE = 2100 # 或是可以輸入:as.integer(readline("input end year:")) 來輸入結束年
year_cnt=0
for (n in yearS:yearE){
  if ( ((!(n %% 4)) && (n %%100)) || (!(n %% 400)) ){
    year_cnt = c(year_cnt,n)
  }
}
year_cnt = year_cnt[-1]
paste("total:", length(year_cnt)," leap years")
## [1] "total: 30  leap years"

case2: 使用等一下要講的函式來簡化以及方便閱讀程式

is_leap_year = function(year1){
  if (((!(year1 %% 4)) && (year1 %%100)) || (!(year1 %% 400)) ){  return(TRUE)
  } else {  return(FALSE)  }  
}
yearS = 1980 # 或是可以輸入:as.integer(readline("input start year:")) 來自己輸入開始年
yearE = 2100 # 或是可以輸入:as.integer(readline("input end year:")) 來輸入結束年
year_cnt=0
for (n in yearS:yearE){
  if (is_leap_year(n)){
    year_cnt = c(year_cnt,n)
    #print("year:",n)
  }
}
year_cnt = year_cnt[-1]
paste("total:", length(year_cnt)," leap years")
## [1] "total: 30  leap years"

Function:函數/函式說明

Function

Function,一般稱為函數,函式,或叫作方程式,簡單說就是把一堆為了相同目的的指令,用一個名稱綁在一起。

例如:

helloworld <- function(name1){
  paste("Hello World,", name1)
}
helloworld("N")
## [1] "Hello World, N"

helloworld:就是函式名稱,之後要使用的話,直接在命令列上輸入helloworld()就可以了 name1:代表這個函式所帶入的參數,這邊取用name1當作參數的名字。

小測驗3:請寫出一個使用快速排序演算法的程式:qsort()

快速排序法(quick-sort)的規則

這個難度有點高,不過相信大家一定還是覺得小蛋糕一片才對。
https://zh.wikipedia.org/zh-tw/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F

步驟為:

+ 1. 從數列中挑出一個元素,稱為"基準"(pivot),
+ 2. 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。
  + 在這個分割結束之後,該基準就處於數列的中間位置。這個稱為分割(partition)操作。
+ 3. 遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。</div>
遞迴的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。 雖然一直遞迴下去,但是這個演算法總會結束,因為在每次的疊代(iteration)中,它至少會把一個元素擺到它最後的位置去。

看不懂嗎?下面有圖解釋:

小結:

幾十億人都震驚的事實,居然現在才能知道….:

作為一個好好用,且知名度超高的統計軟體,其實R內建了許多好用的排序工具,像是 sort(), order(), rank(), 以下會簡略介紹使用方法

sort() / order() / rank()的差別:

sort(): 會傳回一個已經排序列的數值向量。

x = sample.int(100,10) #sample是取亂數的函式,sample.int()強調整數的部份,100代表亂數會在100以內的數值,10則代表取10個數值
sort(x)
##  [1]  8 39 47 50 51 52 57 64 75 89

order(): 會回傳在排序後,各個元素在原始的向量中(x)的排行的值(sorting後的index值)

x = sample.int(100,10)
order(x)
##  [1]  9  7  6  3 10  8  5  4  2  1

rank() : 會回傳排序前,各個元素在原始的向量中(x)的排行的值(sorting前的index值)

x = sample.int(100,10)
rank(x)
##  [1]  9  6  2  3  8  7 10  4  5  1

但是,剛剛的努力都白費了嗎?

比較系統運算時間:

利用system.time()的函式,我們可以知道一個函數,或是一個程式的運算時間。 例如: 亂數產生一個矩陣: m1 = matrix(sample(25002000), 200) m1 = matrix(rnorm(25002000), 200) m1 = matrix(runif(25002000), 200) m1 = matrix(rgamma(25002000), 200)

計算不同的方法的排序時間快慢:

m1 = matrix(sample(2500*2000), 200)
system.time(sort(m1))
##    user  system elapsed 
##   0.844   0.017   0.878
"m1 = matrix(sample(2500*2000), 200) "
"system.time(qsort(m1)) "
## [1] "m1 = matrix(sample(2500*2000), 200) "
## [1] "system.time(qsort(m1)) "

嗯,還是用R內建的工具好了

Debug / Error Handling:除錯,錯誤管理

Debug

寫了一堆程式之後,人都可能被劈腿,手一定有機會出鎚,這時候就需要一些去除錯誤(簡稱:除錯)的工具來幫忙找到錯誤。
幸運的是,R內部就已經提供的debug() / undebug()的工具來一步一步,一行一行的找到問題。

遇到不知道怎麼使用的指令時,在console命令列中輸入?debug,變可以看到debug的說明。
或是想知道有沒有範例可以知道某個指令怎麼用?在console命令列中輸入example(指令),變可以看到該指令的應用

?debug

Error Handling

RBlogger的範例: http://www.r-bloggers.com/error-handling-in-r/

inputs = list(1, 2, 4, -5, 'oops', 0, 10)
for(input in inputs) {
print(paste("log of", input, "=", log(input)))
}

很明顯的,log的參數只能接受不能輸入負數的資料,而且文字資料一定出錯,那我們要怎麼避免?

Error Handling: 使用try()來攔截錯誤,並且讓程式繼續執行

inputs = list(1, 2, 4, -5, 'oops', 0, 10)
for(input in inputs) {
  try(print(paste("log of", input, "=", log(input))))
}
## Warning in log(input): NaNs produced
## [1] "log of 1 = 0"
## [1] "log of 2 = 0.693147180559945"
## [1] "log of 4 = 1.38629436111989"
## [1] "log of -5 = NaN"
## [1] "log of 0 = -Inf"
## [1] "log of 10 = 2.30258509299405"

==> 請參考swirl範例

Error Handling: 使用tryCatch()來顯示錯誤

inputs = list(1, 2, 4, -5, 'oops', 0, 10)
for(input in inputs) {
  tryCatch(print(paste("log of", input, "=", log(input))),
  warning = function(w) {
      print(paste("negative argument", input));
      log(-input)
    },
    error = function(e) {
        print(paste("non-numeric argument", input));
        NaN})
}
## [1] "log of 1 = 0"
## [1] "log of 2 = 0.693147180559945"
## [1] "log of 4 = 1.38629436111989"
## [1] "negative argument -5"
## [1] "non-numeric argument oops"
## [1] "log of 0 = -Inf"
## [1] "log of 10 = 2.30258509299405"

==> 請參考swirl範例

課程預告:

ETL(Extract, Transform and Load ) on R, by Aha

R Visualzation : using ggplot2 , by Mansun/Ben/Kyle

R 火力展示 / 閃電秀 , by c3h3

R 專題實習 , by wush

總結:

文青的時間

There is nothing either good or bad, but thinking makes it so,
quote from Hamlet | Act 2,Scene 2, William Shakespeare

感想:

There is NOTHING either

GOOD  or  BAD

but thinking makes it so.


~ Hamlet | Act 2,Scene 2, William Shakespeare



http://shakespeare.mit.edu/hamlet/hamlet.2.2.htmlHAMLET
Why, then, 'tis none to you; for there is nothing either good or bad, but thinking makes it so: to me, it is a prison



スペック クローズ:SPEC 〜結〜 爻ノ篇

Slide with R Code and Output

summary(cars)
##      speed           dist       
##  Min.   : 4.0   Min.   :  2.00  
##  1st Qu.:12.0   1st Qu.: 26.00  
##  Median :15.0   Median : 36.00  
##  Mean   :15.4   Mean   : 42.98  
##  3rd Qu.:19.0   3rd Qu.: 56.00  
##  Max.   :25.0   Max.   :120.00

Slide with Plot